#include <string.h>
#include <stdlib.h>

#include "psd_auth.h"
#include "psd_modules.h"

#include "compat.h"

typedef struct psd_auth_trusted_user_info_t {
	char *username;
	char *email;
	char *institution;
} psdTrustedUserInfo;

psdTrustedUserInfo *psd_alloc_trusted_user_info();
void psd_free_trusted_user_info(psdTrustedUserInfo *ui);

static int psd_auth_trusted_init();
static int psd_auth_trusted_authenticate(psdConn *conn, psdCreds **ret_creds);
static int psd_auth_trusted_request_authentication (psdSess *sess, psdConn *conn);
const char *psd_auth_trusted_get_user(psdCreds *credentials);
const char *psd_auth_trusted_get_institution(psdCreds *credentials);
const char *psd_auth_trusted_get_email(psdCreds *credentials);
static void psd_auth_trusted_free_credentials(psdCreds *credentials);

static psdAuthenticationHandler psd_auth_trusted_handler = {
	.authenticate = psd_auth_trusted_authenticate,
	.request_authentication = psd_auth_trusted_request_authentication,
	.name = "TRUST",
};

static psdModule psd_auth_trusted_module = {
	.desc = "Trusted Authentication Module",
	.dependencies = "",
	.init = psd_auth_trusted_init
};

psdModule *module_info() {
	return &psd_auth_trusted_module;
}

int psd_auth_trusted_init() {
	return psd_add_authentication_handler(&psd_auth_trusted_handler);
}

const char *psd_auth_trusted_get_user(psdCreds *credentials) {
	psdTrustedUserInfo *ui = (psdTrustedUserInfo *) credentials->private;
	return ui->username;
}

const char *psd_auth_trusted_get_institution(psdCreds *credentials) {
	psdTrustedUserInfo *ui = (psdTrustedUserInfo *) credentials->private;
	return ui->institution;
}

const char *psd_auth_trusted_get_email(psdCreds *credentials) {
	psdTrustedUserInfo *ui = (psdTrustedUserInfo *) credentials->private;
	return ui->email;
}

void psd_auth_trusted_free_credentials(psdCreds *credentials) {
	psdTrustedUserInfo *ui = (psdTrustedUserInfo *) credentials->private;
	psd_free_trusted_user_info(ui);
	free(credentials);
}

int psd_auth_trusted_request_authentication (psdSess *sess, psdConn *conn) {
	xspAuthToken xsp_token;
	char buf[1024];

	snprintf(buf, sizeof(buf), "%s|%s|%s",sess->credentials->get_email(sess->credentials),
			sess->credentials->get_email(sess->credentials),
			sess->credentials->get_institution(sess->credentials));

	xsp_token.token_length = strlen(buf);
	xsp_token.token = buf;

	psd_conn_send_msg(conn, XSP_MSG_AUTH_TOKEN, &xsp_token);

	return 0;
}

int psd_auth_trusted_authenticate(psdConn *conn, psdCreds **ret_creds) {
	psdCreds *creds;
	psdTrustedUserInfo *ui;
	xspMsg *msg;
	xspAuthToken *xsp_token;
	char **columns;
	int column_count;
	char buf[1024];

	msg = psd_conn_get_msg(conn, 0);
	if (!msg) 
		goto error_exit;

	if (msg->type != XSP_MSG_AUTH_TOKEN)
		goto error_exit2;

	xsp_token = msg->msg_body;

	bzero(buf, sizeof(buf));

	if (xsp_token->token_length >= 1024)
		goto error_exit2;

	// with a length < 1024 and a count of 1024, copying to buf will mean
	// that buf is null terminated
	bcopy(xsp_token->token, buf, xsp_token->token_length);

	columns = split(buf, "|", &column_count);
	if (!columns)
		goto error_exit2;

	if (column_count != 3)
		goto error_exit3;

	creds = malloc(sizeof(psdCreds));
	if (!creds)
		goto error_exit3;

	ui = psd_alloc_trusted_user_info();
	if (!ui)
		goto error_exit4;

	ui->username = columns[0];
	ui->email = columns[1];
	ui->institution = columns[2];

	free(columns);

	creds->type = "TRUST";
	creds->private = ui;
	creds->get_user = psd_auth_trusted_get_user;
	creds->get_email = psd_auth_trusted_get_email;
	creds->get_institution = psd_auth_trusted_get_institution;
	creds->free = psd_auth_trusted_free_credentials;

	*ret_creds = creds;

	return 0;

error_exit4:
	free(creds);
error_exit3:
	strlist_free(columns, column_count);
error_exit2:
	xsp_free_msg(msg);
error_exit:
	return -1;
}

psdTrustedUserInfo *psd_alloc_trusted_user_info() {
	psdTrustedUserInfo *ui;

	ui = malloc(sizeof(psdTrustedUserInfo));
	if (!ui)
		goto error_exit;

	bzero(ui, sizeof(psdTrustedUserInfo));

	return ui;

error_exit:
	return NULL;
}

void psd_free_trusted_user_info(psdTrustedUserInfo *ui) {
	if (ui->username)
		free(ui->username);
	if (ui->email)
		free(ui->email);
	if (ui->institution)
		free(ui->institution);
	free(ui);
}
